/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package simhya.simengine.odeevents;

import java.util.Collection;
import org.apache.commons.math.ode.events.EventException;
import org.apache.commons.math.ode.events.EventHandler;

import simhya.model.store.Function;
import simhya.model.store.Predicate;
import simhya.model.store.Store;
import simhya.matheval.SymbolArray;
import simhya.simengine.hybrid.DynamicSwitcher;
import simhya.dataprocessing.EventDataCollector;
import simhya.simengine.ProgressMonitor;


/**
 *
 * @author luca
 */
public class SwitchingEvent implements EventHandler, PrintableEvent {
    private Function eventConditionWhileTrue;
    private Function eventConditionWhileFalse;
    private Function currentEventCondition;
    private Predicate guardWhileTrue;
    private Predicate guardWhileFalse;
    private Predicate currentGuard;
    private boolean status;
    private DynamicSwitcher switcher;
    private boolean isLocalSwitching;
    private int id;
    private int multiplicator;
    
    private SymbolArray vars;
    private boolean useCache;
    private int dimension;
    private EventDataCollector collector;
    String name = "Switching event";
    private boolean printEvent;
    private ProgressMonitor monitor;
    private Collection<Integer> listOfModifiedTriggers;
    private Collection<Integer> listOfModifiedPriorities;
    
    
    public SwitchingEvent(int id, int multiplicator, Function eventConditionWhileTrue, Function eventConditionWhileFalse,
            Predicate guardWhileTrue, Predicate guardWhileFalse,
            DynamicSwitcher switcher, boolean isLocalSwitching, int dimension) {
        this.id = id;
        this.multiplicator = multiplicator;
        this.guardWhileFalse = guardWhileFalse;
        this.guardWhileTrue = guardWhileTrue;
        this.eventConditionWhileTrue = eventConditionWhileTrue;
        this.eventConditionWhileFalse = eventConditionWhileFalse;
        this.isLocalSwitching = isLocalSwitching;
        this.switcher = switcher;
        this.dimension = dimension;
        useCache = false;
        vars = SymbolArray.getNewFastEvalSymbolArray(dimension);
        this.collector = null;
        this.printEvent = false;
        this.monitor = null;
        this.listOfModifiedPriorities = null;
        this.listOfModifiedTriggers = null;
    }
    
     

    public SwitchingEvent(int id, Predicate guard, Function eventCondition, DynamicSwitcher switcher, 
            boolean isLocalSwitching, int dimension) {
        this.id = id;
        this.guardWhileFalse = guard;
        this.guardWhileTrue = guard;
        this.eventConditionWhileTrue = eventCondition;
        this.eventConditionWhileFalse = eventCondition;
        this.switcher = switcher;
        this.isLocalSwitching = isLocalSwitching;
        this.dimension = dimension;
        useCache = false;
        vars = SymbolArray.getNewFastEvalSymbolArray(dimension);
        this.collector = null;
    }
    
    /**
     * Sets a monitor to print event occurrences to
     * @param monitor a ProgressMonitor or null to deactivate the functionality
     */
    public void setPrintEvent(ProgressMonitor monitor) {
        if (monitor != null) {
            this.monitor = monitor;
            this.printEvent = true;
        } else {
            this.monitor = null;
            this.printEvent = false;
        }
    }
    
     public void setCollectorInfo(String name, EventDataCollector collector) {
        this.collector = collector;
        this.name = name;
    }

    public void setUseCache(boolean useCache) {
        this.useCache = useCache;
    }
    
    
    
    
    /**
     * Initializes the switching conditions according to the current value 
     * of variables of the store. To be called only at the beginning of each
     * simulation
     *  
     */
    public void initSwitchingCondition() {
        this.status = (useCache ? 
                this.eventConditionWhileFalse.computeCache() >= 0 : 
                this.eventConditionWhileFalse.compute() >= 0);
        if (status) {
            this.currentEventCondition = this.eventConditionWhileTrue;
            this.currentGuard = this.guardWhileTrue;
        }
        else {
            this.currentEventCondition = this.eventConditionWhileFalse;
            this.currentGuard = this.guardWhileFalse;
        }
        //this.currentEventCondition = this.eventConditionWhileTrue;

        if (this.isLocalSwitching)
            this.switcher.setLocalContinuityStatus(id, status);
        else
            this.switcher.setGlobalContinuityStatus(id, status);
    }

    public int eventOccurred(double t, double[] y, boolean increasing) throws EventException {
        if (this.printEvent)
            monitor.signalEvent(name, t);
        //change state!
        status = !status;
        if (status) {
            this.currentEventCondition = this.eventConditionWhileTrue;
            this.currentGuard = this.guardWhileTrue;
        }
        else {
            this.currentEventCondition = this.eventConditionWhileFalse;
            this.currentGuard = this.guardWhileFalse;
        }
        if (this.isLocalSwitching)
            this.switcher.setLocalContinuityStatus(id, status);
        else
            this.switcher.setGlobalContinuityStatus(id, status);
        if (collector!= null)
            collector.putNextInstantaneousEvent(t, name);
        //even if no variable will be changed, reset state whould guarantee an update
        //of event value in case of a change of predicate
        return EventHandler.RESET_STATE;
    }

    public double g(double t, double[] y) throws EventException {
        vars.setValuesReference(y);
        double x = (useCache ? this.currentEventCondition.computeCache(vars) : 
                this.currentEventCondition.compute(vars));
        return x;
    }

    
    public void resetState(double t, double[] y) throws EventException {
        
    }

    
    public double getPriority(double t, double[] y) throws EventException {
        return 1.0;
    }

    @Override
    public String toString() {
        return "SwitchingEvent{" + "name=" + name + '}';
    }

    public Integer getId() {
        return -(multiplicator + 1 + id);
    }

    public Collection<Integer> getIdsOfEventsWithModifiedPriority() {
        return this.listOfModifiedPriorities;
    }

    public Collection<Integer> getIdsOfEventsWithModifiedTrigger() {
        return this.listOfModifiedTriggers;
    }

    public void setIdsOfEventsWithModifiedPriority(Collection<Integer> list) {
        this.listOfModifiedPriorities = list;
    }

    public void setIdsOfEventsWithModifiedTrigger(Collection<Integer> list) {
        this.listOfModifiedTriggers = list;
    }
    
    
    

    
    public boolean isEnabled(double t, double[] y) {
        vars.setValuesReference(y);
        return (useCache ? currentGuard.evaluateCache(vars) : currentGuard.evaluate(vars)) 
                    || currentGuard.changeContinuously();
    }
    
}
